/*
 * Copyright (C) 2011 by ayoung <ayoung.p@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.peaceb.ruletherain;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

/**
 * Main activity class
 */
public class RuleTheRainActivity extends Activity implements OnClickListener, OnItemClickListener {
    private final int WEATHER_BUTTON_UPDATE = 0;
    private final int WEATHER_BUTTON_WAIT   = 1;
    private final int WEATHER_BUTTON_STOP   = 2;
    
    private final int SERVICE_BUTTON_DISABLE = 0;
    private final int SERVICE_BUTTON_STOP    = 1;
    
    private static final int MENU_ADD        = 0;
    private static final int MENU_HELP       = 1;
    private static final int MENU_ABOUT      = 2;
    
    private static final int CONTEXT_MENU_UPDATE   = 0;
    private static final int CONTEXT_MENU_VIEW_MAP = 1;
    private static final int CONTEXT_MENU_DELETE   = 2;
    
    private static final int DIALOG_ID_DELETING             = 0;
    private static final int DIALOG_ID_LOCATION_ALLOW_CHECK = 1;
    
    private Button                 mButtonWeather;
    private Button                 mButtonStopService;
    private Handler                mHandler;
    private RuleTheRainApplication mApplication;
    private Engine                 mEngine;
    private ListView               mListView;
    private ArrayAdapter<WeatherInfo>     mAdapter;
    private int                    mWeatherButtonState = WEATHER_BUTTON_UPDATE;
    private int                    mServiceButtonState = SERVICE_BUTTON_DISABLE;
    private ProgressDialog         mProgress;
    
    private static final String    PREF_KEY_LOCATION_ALLOW_NEVER_SHOW = "location_allow_never_show";
    
    private static final String    RULETHESKY_PACKAGE_NAME = "com.jce.RuleTheSky";
    private static final String    AGREEMENT_FILE          = "agreement_file";
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Trace.i(this, "onCreate");
        
        setContentView(R.layout.main);
        
        int height = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getHeight();
        View adView = findViewById(R.id.ad);
        adView.setMinimumHeight((int)(height / 10));
        
        mApplication        = (RuleTheRainApplication)getApplication();
        mButtonWeather      = (Button)findViewById(R.id.button_update_weather);
        mButtonStopService  = (Button)findViewById(R.id.button_stop_service);
        mListView           = (ListView)findViewById(R.id.cityList);
        
        mButtonWeather.setOnClickListener(this);
        mButtonStopService.setOnClickListener(this);
        
        mAdapter = new CityListAdapter(this, mApplication.getCityList());
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(this);
        registerForContextMenu(mListView);
        
        mHandler  = new EngineHandler();
        mEngine   = mApplication.getEngine();
        mEngine.setHandler(mHandler);
        
        switch (mEngine.getUpdateState()) {
            case Engine.STATE_NONE:
            case Engine.STATE_STOPPING:
                setWeatherButtonState(WEATHER_BUTTON_WAIT);
                break;
            case Engine.STATE_READY:
                setWeatherButtonState(WEATHER_BUTTON_UPDATE);
                break;
            case Engine.STATE_WORKING:
                setWeatherButtonState(WEATHER_BUTTON_STOP);
                break;
        }
        
        if (mEngine.getServiceState() == Engine.STATE_WORKING) {
            setServiceButtonState(SERVICE_BUTTON_STOP);
        }
        else {
            setServiceButtonState(SERVICE_BUTTON_DISABLE);
        }
        
        if (!isMockLocationAllow()) {
            showCheckSettingPopup();
        }
        
        if (!isRuleTheSkyLocationAllowed()) {
            showDialog(DIALOG_ID_LOCATION_ALLOW_CHECK);
        }
        
        MockLocationService.checkOldProvider(this);
        
        if (C.DEBUG) {
            Util.showToast(this, "DEBUG VERSION");
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        Trace.i(this, "onResume");
        if (mApplication.isDatabaseChanged()) {
            Util.showToast(this, R.string.reload_database);
            mApplication.loadCityList();
        }
        mAdapter.notifyDataSetChanged();
    }

    @Override
    protected void onStop() {
        stopUpdateWeather();
        super.onStop();
        Trace.i(this, "onStop");
    }

    @Override
    protected void onDestroy() {
        Trace.i(this, "onDestroy");
        unregisterForContextMenu(mListView);
        super.onDestroy();
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.button_update_weather:
                if (mWeatherButtonState == WEATHER_BUTTON_UPDATE) {
                    updateWeather();
                }
                else if (mWeatherButtonState == WEATHER_BUTTON_STOP){
                    stopUpdateWeather();
                }
                break;
                
            case R.id.button_stop_service:
                Intent intent = new Intent(this, MockLocationService.class);
                stopService(intent);
                setServiceButtonState(SERVICE_BUTTON_DISABLE);
                break;
        }
    }
    
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        WeatherInfo city = mAdapter.getItem(position);
        Trace.i(city.cityName + " is selected");
        if (!isMockLocationAllow()) {
            showCheckSettingPopup();
            return;
        }
        mEngine.setCurrentCity(city.cityName);
        
        Intent intent = new Intent(this, MockLocationService.class);
        intent.putExtra(MockLocationService.KEY_LATITUDE, city.latitude);
        intent.putExtra(MockLocationService.KEY_LONGITUDE, city.longitude);
        intent.putExtra(MockLocationService.KEY_ALTITUDE, city.altitude);
        intent.putExtra(MockLocationService.KEY_CITY_NAME, city.cityName);
        startService(intent);
        setServiceButtonState(SERVICE_BUTTON_STOP);
        finish();
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_ADD, 0, R.string.add_location).setIcon(android.R.drawable.ic_menu_add);
        menu.add(0, MENU_HELP, 0, R.string.help).setIcon(android.R.drawable.ic_menu_help);
        menu.add(0, MENU_ABOUT, 0, R.string.about).setIcon(android.R.drawable.ic_menu_info_details);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case MENU_ADD:
                stopUpdateWeather();
                startActivity(new Intent(this, AddLocationActivity.class));
                return true;
                
            case MENU_HELP:
                showHelp();
                return true;
                
            case MENU_ABOUT:
                showAbout();
                return true;
        }
        
        return false;
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        if (v.getId() == R.id.cityList) {
            AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
            WeatherInfo weatherInfo = (WeatherInfo)mAdapter.getItem(info.position);
            if (weatherInfo == null) {
                return;
            }
            menu.setHeaderTitle(weatherInfo.cityName);
            menu.add(Menu.NONE, CONTEXT_MENU_UPDATE, Menu.NONE, R.string.update_weather);
            menu.add(Menu.NONE, CONTEXT_MENU_VIEW_MAP, Menu.NONE, R.string.view_map);
            
            if (weatherInfo.userAdd) {
                menu.add(Menu.NONE, CONTEXT_MENU_DELETE, Menu.NONE, R.string.delete);
            }
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        final AdapterView.AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
        final WeatherInfo weatherInfo = (WeatherInfo)mAdapter.getItem(info.position);
        if (weatherInfo == null) {
            return false;
        }
        
        switch (item.getItemId()) {
            case CONTEXT_MENU_UPDATE:
                new Thread(new Runnable() {
                        public void run() {
                            mEngine.getWeatherDataOne(mHandler, weatherInfo);
                        }
                    }
                ).start();
                break;
                
            case CONTEXT_MENU_VIEW_MAP:
                String pos = String.format("geo:%f,%f?z=12", weatherInfo.latitude, weatherInfo.longitude);
                Uri uri = Uri.parse(pos);
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(intent);
                break;
                
            case CONTEXT_MENU_DELETE:
                if (!weatherInfo.userAdd) {
                    return true;
                }
                stopUpdateWeather();
                new AlertDialog.Builder(this)
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .setTitle(weatherInfo.cityName)
                    .setMessage(R.string.delete_confirm)
                    .setNegativeButton(android.R.string.no, null)
                    .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            showDialog(DIALOG_ID_DELETING);
                            new Thread(new Runnable() {
                                @Override
                                public void run() {
                                    deleteUserItem(weatherInfo.id, info.position);
                                    mHandler.sendEmptyMessage(Engine.MSG_DELETE_DONE);
                                }
                            }).start();
                        }
                    }).show();
                break;
        }
        
        return true;
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case DIALOG_ID_DELETING:
                mProgress = new ProgressDialog(this);
                mProgress.setProgressStyle(ProgressDialog.STYLE_SPINNER);
//                mProgress.setCancelable(false);
                mProgress.setMessage(getText(R.string.deleting));
                return mProgress;
                
            case DIALOG_ID_LOCATION_ALLOW_CHECK:
                LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
                final View view = inflater.inflate(R.layout.location_allow_notice, null);
                
                return new AlertDialog.Builder(this)
                    .setIcon(android.R.drawable.ic_dialog_info)
                    .setTitle(R.string.notice)
                    .setView(view)
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            CheckBox checkbox = (CheckBox)view.findViewById(R.id.check_once);
                            if (checkbox.isChecked()) {
                                SharedPreferences pref = 
                                    getSharedPreferences(C.PREFRENCE_NAME, MODE_PRIVATE);
                                SharedPreferences.Editor editor = pref.edit();
                                editor.putBoolean(PREF_KEY_LOCATION_ALLOW_NEVER_SHOW, true);
                                editor.commit();
                            }
                        }
                    }).create();
        }
        return super.onCreateDialog(id);
    }

    /**
     * Delete user item from list from database and listview
     * 
     * @param recordId database record id
     * @param listIndex index from list
     * @return
     */
    private int deleteUserItem(int recordId, int listIndex) {
        String filePath = UserDatabaseHelper.getDatabasePath();
        if (filePath == null) {
            Util.showToast(this, R.string.check_sdcard);
            return C.ERROR;
        }

        UserDatabaseHelper dbHelper = new UserDatabaseHelper(this, filePath);
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        try {
            db.execSQL("DELETE FROM " + UserDatabaseHelper.TABLE_NAME + 
                    " WHERE " + UserDatabaseHelper.ITEM_ID + " = " + recordId);
        }
        catch (SQLiteException e) {
            Trace.e(this, e.toString());
        }
        
        ArrayList<WeatherInfo> list = mApplication.getCityList();
        list.remove(listIndex);
        
        mApplication.setDatabaseModifiedTime(UserDatabaseHelper.getModifiedTime());
        
        return C.SUCCESS;
    }
    
    /**
     * Mock location is not set, inform to user it
     */
    private void showCheckSettingPopup() {
        AlertDialog.Builder dialog = new AlertDialog.Builder(this);
        dialog.setIcon(android.R.drawable.ic_dialog_info);
        dialog.setTitle(R.string.notice);
        dialog.setMessage(R.string.check_location_setting);
        dialog.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent();
                intent.setClassName("com.android.settings", "com.android.settings.DevelopmentSettings");
                try {
                    startActivity(intent);
                }
                catch (ActivityNotFoundException e) {
                    Util.showToast(RuleTheRainActivity.this, R.string.activity_error);
                }
            }
        });
        dialog.setNegativeButton(R.string.no, null);
        dialog.show();
    }
    
    /**
     * Show help
     */
    private void showHelp() {
        startActivity(new Intent(this, HelpActivity.class));
    }
    
    /**
     * Show about
     */
    private void showAbout() {
        startActivity(new Intent(this, AboutActivity.class));
    }
    
    /**
     * Set the weather update button state
     * 
     * @param state
     */
    private void setWeatherButtonState(int state) {
        switch (state) {
            case WEATHER_BUTTON_UPDATE:
                mButtonWeather.setText(R.string.update_weather);
                mButtonWeather.setEnabled(true);
                break;
            case WEATHER_BUTTON_WAIT:
                mButtonWeather.setText(R.string.please_wait);
                mButtonWeather.setEnabled(false);
                break;
            case WEATHER_BUTTON_STOP:
                mButtonWeather.setText(R.string.stop_update_weather);
                mButtonWeather.setEnabled(true);
                break;
        }
        mWeatherButtonState = state;
    }
    
    /**
     * Set the survice stop button
     * 
     * @param state
     */
    private void setServiceButtonState(int state) {
        switch (state) {
            case SERVICE_BUTTON_DISABLE:
                //mButtonStopService.setEnabled(false);
                mButtonStopService.setEnabled(true); // allways true
                break;
            case SERVICE_BUTTON_STOP:
                mButtonStopService.setEnabled(true);
                break;
        }
        mServiceButtonState = state;
    }
    
    /**
     * Update weather info
     */
    private void updateWeather() {
        if (!Util.isNetworkAvail(this)) {
            Util.showToast(this, R.string.check_network);
            return;
        }
        
        mApplication.initWeather();
        mAdapter.notifyDataSetChanged();
        
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                mEngine.getWeatherData(mHandler);
            }
        });
        thread.start();
        setWeatherButtonState(WEATHER_BUTTON_STOP);
    }
    
    /**
     * Stop updating weather info
     */
    private void stopUpdateWeather() {
        if (mEngine.getUpdateState() == Engine.STATE_WORKING) {
            setWeatherButtonState(WEATHER_BUTTON_WAIT);
            mEngine.setUpdateState(Engine.STATE_STOPPING);
        }
    }
    
    /**
     * Get Engine instance
     * 
     * @return
     */
    public Engine getEngine() {
        return mEngine;
    }
    
    /**
     * Check mock location setting
     * 
     * @return
     */
    private boolean isMockLocationAllow() {
        int setting = 0;
        try {
            setting = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION);
        } 
        catch (SettingNotFoundException e) {
            Trace.e(e.toString());
        }
        
        return (setting == 1);
    }
    
    /**
     * Check Rule the Sky location information allowed setting
     * 
     * @return
     */
    private boolean isRuleTheSkyLocationAllowed() {
        boolean ret = false;
        
        SharedPreferences pref = getSharedPreferences(C.PREFRENCE_NAME, MODE_PRIVATE);
        boolean neverShow = pref.getBoolean(PREF_KEY_LOCATION_ALLOW_NEVER_SHOW, false);
        if (neverShow) {
            return true;
        }
        
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
            String dirPath = Util.getExternalFilesDir(this, RULETHESKY_PACKAGE_NAME);
            if (dirPath == null) {
                /* I can't figure out the setting. */
                return false; 
            }
            String filePath = dirPath + "/" + AGREEMENT_FILE;
            try {
                byte[] buff = new byte[10];
                File file = new File(filePath);
                FileInputStream fis = new FileInputStream(file);
                fis.read(buff, 0, 10);
                if (buff[6] == 0x01) {
                    ret = true;
                }
                fis.close();
            } 
            catch (FileNotFoundException e) {
                Trace.w(e.toString());
                return false;
            } 
            catch (IOException e) {
                Trace.w(e.toString());
                return false;
            }
            catch (NullPointerException e) {
                Trace.w(e.toString());
                return false;
            }
        }
        
        return ret;
    }
    
    /**
     * Handler for communication with Engine
     */
    class EngineHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case Engine.MSG_UPDATE:
                    mAdapter.notifyDataSetChanged();
                    break;
                case Engine.MSG_DONE:
                    mAdapter.notifyDataSetChanged();
                    setWeatherButtonState(WEATHER_BUTTON_UPDATE);
                    break;
                case Engine.MSG_DONE_ONE:
                    mAdapter.notifyDataSetChanged();
                    Util.showToast(RuleTheRainActivity.this, R.string.update_done_one);
                    break;
                case Engine.MSG_DELETE_DONE:
                    if (mProgress != null) {
                        mProgress.dismiss();
                    }
                    mAdapter.notifyDataSetChanged();
                    break;
            }
        }
    }
    
    /**
     * City list adapter
     */
    private class CityListAdapter extends ArrayAdapter<WeatherInfo> {
        Context mContext;
        
        private class ViewHolder {
            ImageView image;
            TextView  city;
            TextView  geo;
            TextView  condition;
        }
        
        public CityListAdapter(Context context, List<WeatherInfo> list) {
            super(context, 0, list);
            mContext = context;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder holder;
            if (convertView == null) {
                LayoutInflater inflater = 
                    (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(R.layout.list_item, null);
                holder = new ViewHolder();
                holder.image     = (ImageView)convertView.findViewById(R.id.wether_icon);
                holder.city      = (TextView)convertView.findViewById(R.id.city_name);
                holder.geo       = (TextView)convertView.findViewById(R.id.gps_value);
                holder.condition = (TextView)convertView.findViewById(R.id.condition);
                convertView.setTag(holder);
            }
            
            View view = convertView;
            holder = (ViewHolder)view.getTag();
            
            WeatherInfo city = getItem(position);
            holder.image.setImageResource(WeatherInfo.getIconByWeatherCode(city.weatherCode));
            holder.city.setText(city.cityName);
            holder.geo.setText(city.latitude + ", " + city.longitude);
            holder.condition.setText(city.condition);
            
            if (!city.userAdd) {
                holder.city.setTextColor(Color.WHITE);
            }
            else {
                holder.city.setTextColor(Color.rgb(0x87, 0xce, 0xeb));
            }
            
            if (city.isRain()) {
                holder.condition.setTextColor(Color.WHITE);
                //holder.condition.setBackgroundColor(Color.argb(0xff, 0x00, 0x00, 0x8b));
                holder.condition.setBackgroundResource(R.drawable.weather_info_back);
            }
            else {
                holder.condition.setTextColor(Color.GRAY);
                holder.condition.setBackgroundColor(Color.alpha(0x00));
            }
            
            return view;
        }
    }
}